<html>
<head>
  <title>9.装饰（Decorator）模式</title>
  <basefont face="微软雅黑" size="2" />
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <meta name="exporter-version" content="YXBJ Windows/602628 (zh-CN, DDL); Windows/10.0.0 (Win64); EDAMVersion=V2;"/>
  <style>
    body, td {
      font-family: 微软雅黑;
      font-size: 10pt;
    }
  </style>
</head>
<body>
<a name="22022"/>
<h1>9.装饰（Decorator）模式</h1>
<div>
<table bgcolor="#D4DDE5" border="0">
<tr><td><b>创建时间：</b></td><td><i>2020/5/31 11:22</i></td></tr>
<tr><td><b>更新时间：</b></td><td><i>2020/11/10 8:09</i></td></tr>
<tr><td><b>作者：</b></td><td><i>Long Life Student</i></td></tr>
</table>
</div>
<br/>

<div>
<span><div><div><span style="font-size: 14pt; font-weight: bold;">一、基本描述</span></div><ul><li><div>动态的给一个对象添加一些额外的职责。就增加功能来说，Decorator模式相比生成子类更为灵活。</div></li><li><div>也叫包装器模式（Wrapper）。</div></li><li><div>装饰者和被装饰对象有相同的超类型。</div></li><li><div>可以用一个或多个装饰者包装一个对象。</div><div>既然装饰者和被装饰对象有相同的超类型，所以在任何需要原始对象（被包装的）的场合，可以用装饰过的对象代替它。</div></li><li><div>装饰者可以在所委托被装饰者的行为之前与/或之后，加上自己的行为，以达到特定的目的。</div></li><li><div>对象可以在任何时候被装饰，所以可以在运行时动态的、不限量的用你喜欢的装饰者来装饰对象。</div></li><li><div>装饰与它所装饰的组件接口一致，因此它对使用该组件的客户<span style="font-weight: bold;">透明</span>，它将客户请求<span style="font-weight: bold;">转发</span>给该组件，并且可能在转发前后执行一些<span style="font-weight: bold;">额外的动作</span>。透明性使得客户可以递归的嵌套多个装饰，从而可以添加任意多的功能。</div></li><li><div>如果将代码针对特定种类的具体组件，做一些特殊的事，即代码依赖于特定的具体组件类型，那么装饰者就会导致程序出问题，只有在针对抽象组件类型编程时，才不会因为装饰者而受到影响。但是，如果的确针对特定的具体组件编程，就应该重新思考应用架构，以及装饰者是否适合。</div></li><li><div>能够在不修改任何底层代码的情况下，给你的（或别人的）对象赋予新的职责。</div></li><li><div>在装饰模式中，客户通常不会感觉到装饰过的组件与未装饰组件之间的差异，也不会与装饰产生任何依赖关系。</div></li><li><div>装饰模式可以将所需的功能按照正确的顺序串联起来进行控制。</div></li><li><div>装饰模式将装饰对象的实现和如何使用这个对象分离，每个装饰对象之关心自己的功能，不需要关心如何被添加到对象链中。</div></li><li><div>装饰模式是为已有功能动态的添加更多的功能的一种方式。</div></li><li><div>装饰者可以在被装饰者的行为前面与/或后面加上自己的行为，甚至将被装饰者的行为整个取代掉，而达到特定的目的。</div></li><li><div>使用装饰者模式，必须管理更多的对象，所以犯下编码错误的机会会增加。所以，装饰者可以使用其他类似于工厂或生成器这样的模式创建，此时，它们会“封装得很好”，所以不会有这种问題。</div></li><li><div>原始方法和装饰方法的执行顺序在具体的装饰类中是固定的（即，在 ConcreteDecorator 类中的 Operation，AddedBehavior 和 ConcreteComponent-&gt;Operation的执行顺序是固定的），可以通过方法重载实现多种执行顺序。</div></li><li><div>有书上说装饰模式分为透明装饰模式和半透明装饰模式，但个人认为半透明装饰模式偏离了装饰模式的本意，不应提倡使用。（有关半透明装饰模式，可以直接看<a href="https://github.com/PhycheLiu/DesignPattern_Study-Test/tree/master/Java/src/com/company/decorator/sample01">代码</a>）。</div></li><li><div>装饰模式主要解决：过度使用继承来扩展对象的功能。</div></li><li><div>真实世界的装饰者：Java I/O</div></li><ul><li><div>java.io包内的类太多了，其中很多类都是装饰者。</div></li><li><div>人们第一次接触到Java I/O库时，往往无法轻易地理解它。但是如果他们能认识到这些类都是用来包装 InputStream 的，一切都会变得简单多了。</div></li></ul></ul><div>               <img src="9.装饰（Decorator）模式_files/Image.png" type="image/png" data-filename="Image.png" width="462"/></div><div><br/></div><div><span style="font-size: 14pt; font-weight: bold;">二、图例</span></div><div style="text-align: center;"><img src="9.装饰（Decorator）模式_files/Image [1].png" type="image/png" data-filename="Image.png" width="603"/></div><ul><li><div>Component：定义一个接口对象，可以给这些对象动态的添加职责。最基本、最核心、最原始的接口或抽象类。</div></li><li><div>ConcreteComponent：定义一个对象，可以给这个对象添加一些职责。需要装饰的对象，实际上可能会有很多个 ConcreteComponent。</div></li><li><div>Decorator：维持一个指向 Component 对象的指针，并定义有一个与 Component 接口一致的接口。在其 Operation 当中，需要将操作委托给 ConcreteComponent 来实现（调用 ConcreteComponent 中的 Operation 操作）。实际上是“装饰抽象类”，继承了 Component，从外类来扩展 Component 的功能，但对 Component 来说，无需知道 Decorator 的存在。</div></li><li><div>ConcreteDecorator：向组件添加职责。给 ConcreteComponent 添加职责，将最核心、最原始、最基本的东西装饰成其他东西。</div></li><li><div>Decorator 将请求转发给它的 Component 对象，并有可能在转发请求前后执行一些附加的动作。（用通俗的话说：Decorator 类的 Operation 将操作转发给 ConcreteComponent 的 Operation，转发的过程中可以添加自己特有的操作。）</div></li></ul><div><span style="font-size: 14pt; font-weight: bold;">三、优缺点</span></div><div><span style="font-weight: bold;">优点</span></div><ul><li><div>装饰类和被装饰类可以独立发展，避免耦合。把类中的装饰功能从类中去除，简化原有的类，有效的把类的核心职责和装饰功能区分开了而且可以去除相关类中重复的装饰逻辑。</div></li><li><div>装饰模式是继承关系的一个替代方案。但是装饰模式比继承更有灵活性，不会导致类的个数急剧增加。</div></li><li><div>装饰模式可以动态的扩展一个实现类的功能。</div></li><li><div>可以对一个对象进行多次装饰，通过使用不同的具体装饰类以及这些装饰类的排列组合，可以创造出很多不同行为的组合，得到更多功能对象。</div></li></ul><div><span style="font-weight: bold;">缺点</span></div><ul><li><div>装饰类比较复杂。装饰模式提供了一种比继承更加灵活的解决方案，但是同时也意味着比继承更加容易出错，排错更困难，对于多次装饰的对象，调试时寻找错误可能需要更多级的排查，较为繁琐。</div></li><li><div>使用装饰类进行系统设计时，将会产生很多小对象，这些对象的区别在于它们之间相互连接的方式有所不同，而不是它们的类或者属性值有所不同，大量小对象会影响系统资源，导致系统运行效率下降。</div><div>小对象数量实在太多，可能会造成使用此AP程序员的困扰。但是，了解了装饰者的工作原理，以后当使用别人的大量装饰的API时，就可以很容易地辨别出他们的装饰者类是如何组织的，以方便用包装方式取得想要的行为。但如果使用过度，会让程序变得很复杂。</div></li></ul><div><span style="font-size: 14pt; font-weight: bold;">四、可能的改进</span></div><ul><li><div>如果只有一个 ConcreteComponent 类，则可以将 Component 省略，使用 ConcreteComponent 可以替代 Component 抽象接口，此时，Decorator 是 ComcreteComponent 的一个子类。</div></li><li><div>如果只有一个 ConcreteDecorator，则没有必要建立一个单独的 Decorator 类，而可以将 Decorator 和 ConcreteComponent 的责任合并成一个类。</div></li></ul><div><span style="font-size: 14pt; font-weight: bold;">五、其他讨论</span></div><div><span style="font-size: 12pt;">   </span> <span style="font-size: 10pt;"> 1. 有关设计的讨论</span></div><div><span style="font-size: 10pt;">      一般设计下，当系统需要新功能的时候，是向旧的类中添加新的代码，这些新加的代码通常装饰了原有类的核心职责或主要行为，问题在于，它们在主类中加入了新的字段，新的方法和新的逻辑，从而增加了主类的复杂度，而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。</span></div><div><span style="font-size: 10pt;">      装饰模式针对上述问题给出解决方案，它把每个要装饰的功能放在单独的类中，并让这个类包装它所要装饰的对象，因此，当需要执行特殊行为时，客户代码就可以在运行时根据需要有选择的、按顺序的使用装饰功能包装对象了。</span></div><div><span style="font-size: 10pt;">      在面向对象的设计中，如果超过两层继承，就应该想想是不是设计出问题了，继承的层次越多，以后的维护成本越多。</span></div><ol start="2"><li><div>对装饰模式的更深入的理解</div></li></ol><ul><li><div>尽量保持装饰类的接口与被装饰类的接口相同，客户端可以一致对待装饰前和装饰后的对象。</div></li><li><div>尽量保持具体构建类 ConcreteComponent 是一个“轻”类，即，不要把太多的行为放在具体构建类中，可以通过装饰类对其扩展。</div></li><li><div>Decorator 类在接口上表现为 is-a Component 的继承关系，即 Decorator 类继承了 Component 类所具有的接口，但在实现上却又表现为 has-a Component 的组合关系，即 Decorator 类又使用了另外一个 Component 类。我们可以使用一个或多个 Decorator 对象来“装饰”一个 Component 对象，且装饰后对象仍然是一个 Component 对象。</div></li><li><div>Decorator 模式并非解决“多子类衍生的多继承”问题，Decorator 模式应用的要点在于解决“主题类在多个方向上的扩展功能”——是为“装饰”的含义。Decorator 是在运行时对功能进行组合。</div></li></ul><ol start="3"><li><div>对于合成复用原则的支持</div></li></ol><ul><li><div>Decorator 继承于 Component 类，看似并没有完全的用组合替代继承。</div></li><li><div>这么做的重点在于，装饰者和被装饰者必须是一样的类型，也就是有共同的超类，这是相当关键的地方。在这里，利用继承达到“类型匹配”，而不是利用继承获得“行为”。</div></li><li><div>将 Decorator 同 ConcreteComponent 组合时，就是在加入新的行为，所得到的新行为，并不是继承自超类，而是由组合对象得来的。</div></li><li><div>总结一下，装饰者模式当中有继承，但 Decorator 继承 Component 抽象类，是为了有正确的类型，而不是继承它的行为。行为来自装饰者和基础组件，或与其他装饰者之间的组合关系。</div></li></ul><div><span style="font-size: 14pt; font-weight: bold;">六、个人理解</span></div><ul><li><div>装饰模式应用很广泛，会非常有用，是继承的很好的替代。</div></li><li><div>可以无限装饰同一个对象，可以动态的给对象增加功能，非常有用。</div></li><li><div>装饰模式致力于将装饰功能从主类中区分开来，更多针对多余的、辅助的功能，将这些功能从主类中移除，单独成为装饰类（ConcreteDecorator），这样保证了主类的简单、纯洁，也保证了装饰功能的动态可扩展。</div></li><li><div>在实际应用中，应当尽量减少对 ConcreteComponent 对象使用装饰类的数量，以便降低系统的复杂度和维护的难度。</div></li></ul><div><br clear="none"/></div><div><br clear="none"/></div></div><div><br/></div></span>
</div></body></html> 